import type { PaginationProps } from '@/components/Table/src/types/pagination';
import { ROW_KEY, PAGE_SIZE } from '@/components/Table/src/const';
import type { SorterResult } from '@/components/Table/src/types/table';

import {
  ref,
  unref,
  ComputedRef,
  computed,
  onMounted,
  watch,
  reactive,
  Ref,
  watchEffect,
} from 'vue';
import { useTimeoutFn } from '@vben/hooks';
import { buildUUID } from '@/utils/uuid';
import { isFunction, isBoolean, isObject, isArray } from '@/utils/is';
import { get, cloneDeep } from 'lodash-es';
import { parseRowKeyValue } from '@/components/Table/src/helper';
import type { Key } from 'ant-design-vue/lib/table/interface';

import type { AdTableProps, AdFetchParams } from '../types/adTable';
import { handleFilterGroup } from '../types/adTable';

import { FilterRule, FilterGroup, SortCondition, PageRequest } from '@/utils/common/types';
import { FilterOperateEnum } from '@/enums/filterOperateEnum';
import { ListSortDirectionEnum } from '@/enums/listSortDirectionEnum';

interface ActionType {
  getPaginationInfo: ComputedRef<boolean | PaginationProps>;
  setPagination: (info: Partial<PaginationProps>) => void;
  setLoading: (loading: boolean) => void;
  getSearchInfo: () => FilterGroup;
  clearSelectedRowKeys: () => void;
  tableData: Ref<Recordable[]>;
}

interface SearchState {
  sortInfo: Recordable;
  filterInfo: Record<string, string[]>;
}

export function useAdDataSource(
  propsRef: ComputedRef<AdTableProps>,
  {
    tableData,
    getPaginationInfo,
    setPagination,
    setLoading,
    getSearchInfo,
    clearSelectedRowKeys,
  }: ActionType,
  emit: EmitType,
) {
  const searchState = reactive<SearchState>({
    sortInfo: {},
    filterInfo: {},
  });
  const dataSourceRef = ref<Recordable[]>([]);
  const rawDataSourceRef = ref<Recordable>({});
  const searchInfoRef = ref<Recordable>({});

  watchEffect(() => {
    tableData.value = unref(dataSourceRef);
  });

  watch(
    () => unref(propsRef).dataSource,
    () => {
      const { dataSource, api } = unref(propsRef);
      !api && dataSource && (dataSourceRef.value = dataSource);
    },
    {
      immediate: true,
    },
  );

  function handleTableChange(
    pagination: PaginationProps,
    filters: Partial<Recordable<string[]>>,
    sorter: SorterResult,
  ) {
    const { clearSelectOnPageChange, sortFn, filterFn } = unref(propsRef);
    if (clearSelectOnPageChange) {
      clearSelectedRowKeys();
    }
    setPagination(pagination);

    const params: Recordable = {};
    if (sorter && isFunction(sortFn)) {
      const sortInfo = sortFn(sorter);
      searchState.sortInfo = sortInfo;
      params.sortInfo = sortInfo;
    }

    if (filters && isFunction(filterFn)) {
      const filterInfo = filterFn(filters);
      searchState.filterInfo = filterInfo;
      params.filterInfo = filterInfo;
    }
    fetch(params);
  }

  function setTableKey(items: any[]) {
    if (!items || !Array.isArray(items)) return;
    items.forEach((item) => {
      if (!item[ROW_KEY]) {
        item[ROW_KEY] = buildUUID();
      }
      if (item.children && item.children.length) {
        setTableKey(item.children);
      }
    });
  }

  const getAutoCreateKey = computed(() => {
    return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey;
  });

  const getRowKey = computed(() => {
    const { rowKey } = unref(propsRef);
    return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
  });

  const getDataSourceRef: Ref<Recordable<any>[]> = ref([]);

  watch(
    () => dataSourceRef.value,
    () => {
      const dataSource = unref(dataSourceRef);
      if (!dataSource || dataSource.length === 0) {
        getDataSourceRef.value = unref(dataSourceRef);
      }
      if (unref(getAutoCreateKey)) {
        const firstItem = dataSource[0];
        const lastItem = dataSource[dataSource.length - 1];

        if (firstItem && lastItem) {
          if (!firstItem[ROW_KEY] || !lastItem[ROW_KEY]) {
            const data = cloneDeep(unref(dataSourceRef));
            data.forEach((item) => {
              if (!item[ROW_KEY]) {
                item[ROW_KEY] = buildUUID();
              }
              if (item.children && item.children.length) {
                setTableKey(item.children);
              }
            });
            dataSourceRef.value = data;
          }
        }
      }
      getDataSourceRef.value = unref(dataSourceRef);
    },
    {
      deep: true,
      immediate: true,
    },
  );

  async function updateTableData(index: number, key: Key, value: any) {
    const record = dataSourceRef.value[index];
    if (record) {
      dataSourceRef.value[index][key] = value;
    }
    return dataSourceRef.value[index];
  }

  function updateTableDataRecord(keyValue: Key, record: Recordable): Recordable | undefined {
    const row = findTableDataRecord(keyValue);

    if (row) {
      for (const field in row) {
        if (Reflect.has(record, field)) row[field] = record[field];
      }
      return row;
    }
  }

  function deleteTableDataRecord(keyValues: Key | Key[]) {
    if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
    const delKeyValues = !Array.isArray(keyValues) ? [keyValues] : keyValues;

    function deleteRow(data, keyValue) {
      const row: { index: number; data: [] } = findRow(data, keyValue);
      if (row === null || row.index === -1) {
        return;
      }
      row.data.splice(row.index, 1);

      function findRow(data, keyValue) {
        if (data === null || data === undefined) {
          return null;
        }
        for (let i = 0; i < data.length; i++) {
          const row = data[i];
          if (parseRowKeyValue(unref(getRowKey), row) === keyValue) {
            return { index: i, data };
          }
          if (row.children?.length > 0) {
            const result = findRow(row.children, keyValue);
            if (result != null) {
              return result;
            }
          }
        }
        return null;
      }
    }

    for (const keyValue of delKeyValues) {
      deleteRow(dataSourceRef.value, keyValue);
      deleteRow(unref(propsRef).dataSource, keyValue);
    }
    setPagination({
      total: unref(propsRef).dataSource?.length,
    });
  }

  function insertTableDataRecord(
    record: Recordable | Recordable[],
    index?: number,
  ): Recordable[] | undefined {
    // if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
    index = index ?? dataSourceRef.value?.length;
    const _record = isObject(record) ? [record as Recordable] : (record as Recordable[]);
    unref(dataSourceRef).splice(index, 0, ..._record);
    return unref(dataSourceRef);
  }

  function findTableDataRecord(keyValue: Key) {
    if (!dataSourceRef.value || dataSourceRef.value.length === 0) return;
    const { childrenColumnName = 'children' } = unref(propsRef);

    const findRow = (array: any[]) => {
      let ret;
      array.some(function iter(r) {
        if (parseRowKeyValue(unref(getRowKey), r) === keyValue) {
          ret = r;
          return true;
        }
        return r[childrenColumnName] && r[childrenColumnName].some(iter);
      });
      return ret;
    };

    return findRow(dataSourceRef.value);
  }

  /** 重新定义取数逻辑，以配合后台 todo:cmm */
  async function fetch(opt?: AdFetchParams) {
    //TODO:定义请求体
    let pageRequest: PageRequest = new PageRequest();

    // const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm, pagination } = unref(propsRef);
    const { api, searchInfo, beforeFetch, afterFetch, useSearchForm, pagination, columns } =
      unref(propsRef);
    if (!api || !isFunction(api)) return;
    try {
      setLoading(true);
      // const { pageField, sizeField, listField, totalField } = Object.assign(
      //   {},
      //   FETCH_SETTING,
      //   fetchSetting
      // );
      // let pageParams: Recordable = {};
      const listField = 'Rows';
      const totalField = 'Total';

      const { current = 1, pageSize = PAGE_SIZE } = unref(getPaginationInfo) as PaginationProps;

      if ((isBoolean(pagination) && !pagination) || isBoolean(getPaginationInfo)) {
        // pageParams = {};
      } else {
        // pageParams[pageField] = (opt && opt.page) || current;
        // pageParams[sizeField] = pageSize;

        //TODO:设置请求体分页条件信息（页索引、页大小）
        pageRequest.PageCondition.PageIndex = (opt && opt.page) || current;
        pageRequest.PageCondition.PageSize = pageSize;
      }

      //TODO:设置请求体分页条件信息（排序条件组）
      const { sortInfo, filterInfo } = searchState;
      const sortConditions = handleSortConditions(sortInfo || opt?.sortInfo);
      pageRequest.PageCondition.SortConditions = [
        ...pageRequest.PageCondition.SortConditions,
        ...sortConditions,
      ];
      // console.log('排序条件组', pageRequest.PageCondition.SortConditions);

      //TODO:设置请求体查询条件组
      if (filterInfo) {
        const filterGroup = handlefilterGroup(filterInfo);
        // console.log('fetch-filterInfo', { filterInfo, filterGroup });

        if (filterGroup?.Groups?.length > 0 || filterGroup?.Rules?.length > 0) {
          const g = handleFilterGroup(filterGroup, columns);
          if (g.Operate && !pageRequest.FilterGroup.Groups.find((m) => m === g)) {
            pageRequest.FilterGroup.Groups.push(g);
          }
        }
      }
      if (opt?.filterInfo) {
        const filterGroup = handlefilterGroup(opt?.filterInfo);
        // console.log('fetch-opt?.filterInfo', { filterInfo: opt?.filterInfo, filterGroup });

        if (filterGroup?.Groups?.length > 0 || filterGroup?.Rules?.length > 0) {
          const g = handleFilterGroup(filterGroup, columns);
          if (g.Operate && !pageRequest.FilterGroup.Groups.find((m) => m === g)) {
            pageRequest.FilterGroup.Groups.push(g);
          }
        }
      }
      if (searchInfo) {
        //console.log('fetch-searchInfo', searchInfo);
        if (searchInfo?.Groups?.length > 0 || searchInfo?.Rules?.length > 0) {
          const g = handleFilterGroup(searchInfo, columns);
          if (g.Operate && !pageRequest.FilterGroup.Groups.find((m) => m === g)) {
            pageRequest.FilterGroup.Groups.push(g);
          }
        }
      }
      if (opt?.searchInfo) {
        //console.log('fetch-opt?.searchInfo', opt?.searchInfo);
        if (opt?.searchInfo?.Groups?.length > 0 || opt?.searchInfo?.Rules?.length > 0) {
          const g = handleFilterGroup(opt?.searchInfo, columns);
          if (g.Operate && !pageRequest.FilterGroup.Groups.find((m) => m === g)) {
            pageRequest.FilterGroup.Groups.push(g);
          }
        }
      } else if (useSearchForm) {
        const group = getSearchInfo() as FilterGroup;
        if (group?.Groups?.length > 0 || group?.Rules?.length > 0) {
          const g = handleFilterGroup(group, columns);
          if (g.Operate && !pageRequest.FilterGroup.Groups.find((m) => m === g)) {
            pageRequest.FilterGroup.Groups.push(g);
          }
        }
      }

      // let params: Recordable = merge(
      //   pageParams,
      //   useSearchForm ? getFieldsValue() : {},
      //   searchInfo,
      //   opt?.searchInfo ?? {},
      //   defSort,
      //   sortInfo,
      //   filterInfo,
      //   opt?.sortInfo ?? {},
      //   opt?.filterInfo ?? {},
      // );
      // if (beforeFetch && isFunction(beforeFetch)) {
      //   params = (await beforeFetch(params)) || params;
      // }
      if (beforeFetch && isFunction(beforeFetch)) {
        pageRequest = (await beforeFetch(pageRequest)) || pageRequest;
      }
      // console.log('fetch', pageRequest);
      searchInfoRef.value = pageRequest;
      const res = await api(pageRequest);
      rawDataSourceRef.value = res;

      const isArrayResult = Array.isArray(res);

      let resultItems: Recordable[] = isArrayResult ? res : get(res, listField);
      const resultTotal: bigint = isArrayResult ? BigInt(res.length) : BigInt(get(res, totalField));

      // 假如数据变少，导致总页数变少并小于当前选中页码，通过getPaginationRef获取到的页码是不正确的，需获取正确的页码再次执行
      if (resultTotal > 0) {
        const currentTotalPage = Math.ceil(Number(resultTotal) / pageSize);
        if (current > currentTotalPage) {
          setPagination({
            current: currentTotalPage,
          });
          return await fetch(opt);
        }
      }

      if (afterFetch && isFunction(afterFetch)) {
        resultItems = (await afterFetch(resultItems)) || resultItems;
      }
      dataSourceRef.value = resultItems;
      setPagination({
        total: Number(resultTotal) || 0,
      });
      if (opt && opt.page) {
        setPagination({
          current: opt.page || 1,
        });
      }
      emit('fetch-success', {
        items: unref(resultItems),
        total: resultTotal,
      });
      return resultItems;
    } catch (error) {
      emit('fetch-error', error);
      dataSourceRef.value = [];
      setPagination({
        total: 0,
      });
    } finally {
      setLoading(false);
    }
  }

  function setTableData<T = Recordable>(values: T[]) {
    dataSourceRef.value = values as any[];
  }

  function getDataSource<T = Recordable>() {
    return getDataSourceRef.value as T[];
  }

  function getRawDataSource<T = Recordable>() {
    return rawDataSourceRef.value as T;
  }

  async function reload(opt?: AdFetchParams) {
    return await fetch(opt);
  }

  function getSearchInfoFn<T = Recordable>() {
    return searchInfoRef.value as T;
  }

  onMounted(() => {
    useTimeoutFn(() => {
      unref(propsRef).immediate && fetch();
    }, 16);
  });

  /** 设置排序条件集合 */
  function handleSortConditions(sortInfo: Recordable): SortCondition[] {
    const sortConditions: SortCondition[] = [];
    if (!sortInfo) {
      return sortConditions;
    }
    if (isArray(sortInfo)) {
      sortInfo.forEach((item) => {
        if (item?.field && item?.order) {
          sortConditions.push({
            // 排序字段（The sort field passed to the backend you）
            SortField: item.field,
            // 排序方式 asc/desc（Sorting method passed to the background asc/desc）
            ListSortDirection:
              (item.order as string)?.toLowerCase() === 'descend'
                ? ListSortDirectionEnum.Descending
                : ListSortDirectionEnum.Ascending,
          });
        }
      });
    } else {
      const { field, order } = sortInfo;
      if (field && order) {
        sortConditions.push({
          // 排序字段（The sort field passed to the backend you）
          SortField: field,
          // 排序方式 asc/desc（Sorting method passed to the background asc/desc）
          ListSortDirection:
            (order as string)?.toLowerCase() === 'descend'
              ? ListSortDirectionEnum.Descending
              : ListSortDirectionEnum.Ascending,
        });
      }
    }
    // console.log('handleSortConditions', sortConditions);
    return sortConditions;
  }
  /** 设置查询条件组 */
  function handlefilterGroup(filterInfo: Partial<Recordable<string[]>>) {
    const filterGroup: FilterGroup = new FilterGroup();
    filterGroup.Operate = FilterOperateEnum.And;
    for (const filter in filterInfo) {
      const filterVals = filterInfo[filter];
      if (filterVals) {
        if (Array.isArray(filterVals) && filterVals.length > 0) {
          const subGroup: FilterGroup = new FilterGroup();
          subGroup.Operate = FilterOperateEnum.Or;
          for (const filterVal of filterVals) {
            const filterRule: FilterRule = new FilterRule(
              filter,
              filterVal,
              FilterOperateEnum.Equal,
            );
            subGroup.Rules = [...subGroup.Rules, ...[filterRule]];
          }
          if (subGroup.Rules.length > 0) {
            filterGroup.Groups = [...filterGroup.Groups, ...[subGroup]];
          }
        } else {
          const filterRule2: FilterRule = new FilterRule(
            filter,
            filterVals,
            FilterOperateEnum.Equal,
          );
          filterGroup.Rules = [...filterGroup.Rules, ...[filterRule2]];
        }
      }
    }
    // console.log('filterGroup', filterGroup);
    return filterGroup;
  }

  return {
    getDataSourceRef: computed(() => getDataSourceRef.value),
    getDataSource,
    getRawDataSource,
    searchInfoRef,
    getSearchInfo: getSearchInfoFn,
    getRowKey,
    setTableData,
    getAutoCreateKey,
    fetch,
    reload,
    updateTableData,
    updateTableDataRecord,
    deleteTableDataRecord,
    insertTableDataRecord,
    findTableDataRecord,
    handleTableChange,
  };
}
